# !/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket
import xml.etree.ElementTree as EleTree
from util.lib import pretty_xml, transfor_data_set_to_node_set
from util.topo_adapter import get_topo_set
from util.flow_adapter import get_route_path
from util.solution_adapter import get_plan_set


def _init_map_tbl_xml(node_set, map_tbl_xml):
    root = EleTree.Element('map_tbl')

    for entry in node_set:
        node_ele = EleTree.SubElement(root, 'node')
        node_id_ele = EleTree.SubElement(node_ele, 'node_id')
        node_id_ele.text = str(entry['node_id'])
        for flow in entry['data_set']:
            entry_ele = EleTree.SubElement(node_ele, 'entry')
            flow_id_ele = EleTree.SubElement(entry_ele, 'flow_id')
            flow_id_ele.text = str(flow['flow_id'])
            valid_ele = EleTree.SubElement(entry_ele, 'valid')
            valid_ele.text = '1'
            reserved_ele = EleTree.SubElement(entry_ele, 'reserved')
            reserved_ele.text = '0'
            five_tuple_mask_ele = EleTree.SubElement(entry_ele, 'five_tuple_mask')
            five_tuple_mask_ele.text = '0b'+ str(flow['five_tuple_mask'])
            ip_protocol_ele = EleTree.SubElement(entry_ele, 'ip_protocol')
            ip_protocol_ele.text = str(flow['ip_protocol'])
            dport_ele = EleTree.SubElement(entry_ele, 'dst_port')
            dport_ele.text = str(flow['dst_port'])
            sport_ele = EleTree.SubElement(entry_ele, 'src_port')
            sport_ele.text = str(flow['src_port'])
            dst_ip_ele = EleTree.SubElement(entry_ele, 'dst_ip')
            dst_ip = flow['dst_ip']
            dst_ip = ".".join([str(dst_ip >> x & 0xff) for x in [24, 16, 8, 0]])
            dst_ip_ele.text = dst_ip
            src_ip_ele = EleTree.SubElement(entry_ele, 'src_ip')
            src_ip = flow['src_ip']
            src_ip = ".".join([str(src_ip >> x & 0xff) for x in [24, 16, 8, 0]])
            src_ip_ele.text = src_ip
            reserved_ele = EleTree.SubElement(entry_ele, 'reserved')
            reserved_ele.text = '0'
            priority_ele = EleTree.SubElement(entry_ele, 'priority')
            priority_ele.text = str(flow['priority'])
            redundant_ele = EleTree.SubElement(entry_ele, 'redundant')
            redundant_ele.text = str(flow['redundant'])
            dbufid_ele = EleTree.SubElement(entry_ele, 'dbufid')
            dbufid_ele.text = str(flow['dbufid'])

    tree = EleTree.ElementTree(root)
    pretty_xml(root, '\t', '\n')
    tree.write(map_tbl_xml,
               encoding='utf-8',
               xml_declaration=True)
    return


def _update_src_node_id(topo_set, plan_set):
    tmp_set = plan_set
    plan_set = []

    for entry in tmp_set:
        link_id = entry['link_id']
        for link in topo_set:
            if link['link_id'] == link_id:
                entry.update({
                    'src_node_id': link['src_node_id'],
                })
                plan_set.append(entry)
                break
    return plan_set


def _update_map_set(plan_set, map_set):
    tmp_set = map_set
    map_set = []
    for entry in tmp_set:
        flow_id = entry['flow_id']
        node_id = entry['node_id']
        for link in plan_set:
            if link['flow_id'] == flow_id and link['src_node_id'] == node_id:
                entry.update({
                    'priority': link['priority'],
                })
                map_set.append(entry)
                break

    return map_set


def _get_map_set(flow_xml, route_set):
    map_set = []

    # 解析每一条流量
    # 在每一条流量的源节点加上流映射表表项
    tree = EleTree.parse(flow_xml)
    flow_set_ele = tree.findall('flow')
    for flow_ele in flow_set_ele:
        # 判断这条流量是否需要进行映射
        # 每条流都生成映射表
        fl_api_flag = flow_ele.findtext('fl_api_flag')
        # # 如果该流量由FL API进行映射，那么不需要添加到映射表内
        # if fl_api_flag == '1':
        #     continue

        entry = {}
        flow_id_ele = flow_ele.find("stream_id")
        flow_id = int(flow_id_ele.text)
        entry.update({'flow_id': flow_id})
        entry.update({'dbufid': flow_id})

        # 五元组信息
        src_ip = flow_ele.findtext('src_ip')
        src_ip = int.from_bytes(socket.inet_aton(src_ip), 'big')
        entry.update({'src_ip': src_ip})
        dst_ip = flow_ele.findtext('dst_ip')
        dst_ip = int.from_bytes(socket.inet_aton(dst_ip), 'big')
        entry.update({'dst_ip': dst_ip})
        src_port = flow_ele.findtext('src_port')
        dst_port = flow_ele.findtext('dst_port')
        ip_protocol = flow_ele.findtext('ip_protocol')
        fivetuple_mask = flow_ele.findtext('fivetuple_mask')
        entry.update({'src_port': src_port,
                      'dst_port': dst_port,
                      'five_tuple_mask': fivetuple_mask,
                      'ip_protocol': ip_protocol
                      })

        redundant_flag = flow_ele.findtext('redundant')
        redundant_flag = int(redundant_flag)
        entry.update({'redundant': redundant_flag})

        # 找到这条流的第一跳节点（网卡）
        for route_entry in route_set:
            if flow_id == route_entry['flow_id']:
                src_node_id = route_entry['src_node_id']
                entry.update({'node_id': int(src_node_id)})
                map_set.append(entry)
                continue

    return map_set


def assemble_map_tbl(flow_xml, plan_xml, topo_xml, map_tbl_xml):
    # 1.根据topo_xml，获取topo_set
    topo_set = get_topo_set(topo_xml)
    print("1. topo_set: ")
    [print(entry) for entry in topo_set]
    # 2.获取流的路由路径
    route_set = get_route_path(flow_xml, topo_set)
    print("2. route_set: ")
    [print(entry) for entry in route_set]

    # 3.获取plan_set
    plan_set = get_plan_set(plan_xml)
    print("3. plan_set: ")
    [print(entry) for entry in plan_set]

    # 4.获取流映射表信息，除优先级字段外
    map_set = _get_map_set(flow_xml, route_set)
    print("4. map_set: ")
    [print(entry) for entry in map_set]

    # 5.更新plan_set，链路源节点
    plan_set = _update_src_node_id(topo_set, plan_set)
    print("5. update src_node to plan_set: ")
    [print(entry) for entry in plan_set]

    # 6.更新map_set，增加优先级字段
    map_set = _update_map_set(plan_set, map_set)
    print("6. update priority to map_set: ")
    [print(entry) for entry in map_set]

    # 7.将属于同一节点的映射表整合到一起
    node_set = transfor_data_set_to_node_set(map_set)
    print("7. node_set: ")
    [print(entry) for entry in node_set]

    # 生成xml
    _init_map_tbl_xml(node_set, map_tbl_xml)

    return


def main():
    flow_xml = '../input/flow_feature.xml'
    plan_xml = '../input/solution.xml'
    topo_xml = '../input/topo_feature.xml'
    map_tbl_xml = '../output/tbl/map_tbl.xml'
    assemble_map_tbl(flow_xml, plan_xml, topo_xml, map_tbl_xml)
    return


if __name__ == '__main__':
    main()
